/* eslint-disable @typescript-eslint/promise-function-async, @typescript-eslint/no-unused-vars */

/**
 * JSSDK 客户端，运行在客户自定义页面中
 * 相关文档见 README.md
 */
import type { JSSDKMessgae } from './host'

/**
 * 注册全局变量
 */
declare global {
  interface Window {
    QunTool: ReturnType<typeof makeSDKClient>
    wx: ReturnType<typeof makeFakeWXJSSDK>
    inQunTool?: unknown
  }
}

window.QunTool = makeSDKClient()
window.wx = makeFakeWXJSSDK()

/**
 * 创建 SDK 实例
 */
function makeSDKClient() {
  /**
   * 环境变量
   * URL 中带 in-quntool=1，或者提前在页面中注入 window.inQunTool = 1 变量，会让页面判定为处于 QunTool 中
   */
  const inQunTool = location.href.includes('in-quntool=1') || window.inQunTool === 1
  const parent =
    (window.opener as Window | null) ?? (window.parent !== window.self ? window.parent : null)

  /**
   * 实现收发消息
   */
  const onceEvents: { [type: string]: ((data: unknown) => void)[] } = {} // 触发一次就会被移除的回调
  const events: { [type: string]: (data: unknown) => void } = {} // 持续有效的回调
  function registerOnce<T>(type: string, handler: (data: T) => void) {
    if (onceEvents[type]) onceEvents[type]!.push(handler as (data: unknown) => void)
    else onceEvents[type] = [handler as (data: unknown) => void]
  }
  function registerEvent<T>(type: string, handler: (data: T) => void) {
    events[type] = handler as (data: unknown) => void
  }

  function handleMessage(message: MessageEvent<unknown>) {
    const raw = message.data
    if (typeof raw !== 'object' || !raw || !('quntoolJSSDK' in raw)) return
    const { type, data } = raw as JSSDKMessgae
    console.debug('[quntool-jssdk] 收到消息：', raw)

    if (!events[type] && (!onceEvents[type] || !onceEvents[type]!.length))
      return void console.warn(`不认识的消息类型：${type}`, raw)
    if (events[type]) events[type]!(data)
    if (onceEvents[type] && onceEvents[type]!.length) {
      const handler = onceEvents[type]!.shift()!
      handler(data)
    }
  }
  window.addEventListener('message', handleMessage)

  function sendMessage<T>(type: string, data?: T) {
    parent?.postMessage({ type, data, quntoolJSSDK: true } as JSSDKMessgae<T>, '*')
  }

  function invoke<T, R>(type: string, data?: T): Promise<R> {
    return new Promise<R>(resolve => {
      sendMessage(type, data)
      registerOnce(type, (response: R) => resolve(response))
    })
  }

  /**
   * 执行与 Host 间的连接测试
   */
  const ready = new Promise<void>(resolve => {
    if (!inQunTool) return

    console.debug('[quntool-jssdk] 开始连接测试...')
    void invoke('connect-test').then(() => {
      console.debug('[quntool-jssdk] 连接测试：收到 Host 响应')
      void invoke('connect-confirm').then(() => {
        console.debug('[quntool-jssdk] 连接成功')
        resolve()
      })
    })
  })

  return {
    inQunTool,
    ready,
    invoke,
  }
}

/**
 * 生成模拟的企业微信 JSSDK
 * https://work.weixin.qq.com/api/doc/90000/90136/90512
 * https://work.weixin.qq.com/api/doc/10029
 */
function makeFakeWXJSSDK() {
  function config() {}
  function agentConfig() {}

  function ready(callback: () => void) {
    callback()
  }

  function error() {}

  function invoke(wxapi: string, params: unknown, callback: (res: unknown) => void) {
    if (wxapi === 'getCurExternalContact') {
      type Res =
        | { success: true; data: { externalUserId: string } }
        | { success: false; error: string }
      void window.QunTool.invoke<void, Res>('get-customer-info').then(quntoolRes => {
        if (quntoolRes.success) {
          callback({
            err_msg: 'getCurExternalContact:ok',
            userId: quntoolRes.data.externalUserId,
          })
        } else {
          callback({
            err_msg: quntoolRes.error,
          })
        }
      })
    } else if (wxapi === 'sendChatMessage') {
      interface BaseMessage {
        enterChat: boolean
      }
      interface TextMessage extends BaseMessage {
        msgtype: 'text'
        text: { content: string }
      }
      interface ImageMessage extends BaseMessage {
        msgtype: 'image'
        text: { mediaId: string }
      }
      interface VideoMessage extends BaseMessage {
        msgtype: 'video'
        text: { mediaId: string }
      }
      interface FileMessage extends BaseMessage {
        msgtype: 'file'
        text: { mediaId: string }
      }
      interface NewsMessage extends BaseMessage {
        msgtype: 'news'
        news: {
          link: string
          title: string
          desc: string
          imgUrl: string
        }
      }
      interface MiniprogramMessage extends BaseMessage {
        msgtype: 'miniprogram'
        miniprogram: {
          appid: string
          title: string
          imgUrl: string
          page: string
        }
      }
      // TODO: 目前因没找到办法通过 mediaid 获取资源内容而无法实现此接口
    } else {
      console.error(`[quntool-jssdk] 不支持的 invoke 类型：wx.invoke('${wxapi}')`)
    }
  }

  const wx = {
    config,
    agentConfig,
    ready,
    error,
    invoke,
  }

  return new Proxy(wx, {
    get(target, prop, receiver) {
      if (prop in target) return Reflect.get(target, prop, receiver) // eslint-disable-line @typescript-eslint/no-unsafe-return
      console.error(`[quntool-jssdk] 不支持的企业微信 JSSDK 接口：wx.${String(prop)}`)
      return () => {}
    },
  })
}
